package com.ybliang.plat.common.rpc.consumer;

import com.ybliang.plat.common.rpc.consumer.config.RpcClientProperties;
import com.ybliang.plat.common.rpc.consumer.netty.NettyClient;
import com.ybliang.plat.common.rpc.core.RpcRequest;
import com.ybliang.plat.common.rpc.core.RpcResponse;
import com.ybliang.plat.common.rpc.protocol.json.JSON;
import com.ybliang.plat.serviceapp.common.exception.BusinessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * RpcFramework
 * @author liangyb1
 */
@Deprecated
@Component
@EnableConfigurationProperties({RpcClientProperties.class})
public class RpcFramework {

    public static Logger LOGGER = LoggerFactory.getLogger(RpcFramework.class);

    public static final Map<String, Object> rpcProxyObjectMap = new ConcurrentHashMap<>();

    @Autowired
    RpcClientProperties rpcClientProperties;

    /**
     * 暴露服务
     * @param service
     * @param port
     */
//    public static void export(Object service, int port) throws Exception {
//
//        ServerSocket serverSocket = new ServerSocket(port);
//        for (;;){
//            try {
//                Socket socket = serverSocket.accept();
//                new Thread(new Runnable() {
//                    @Override
//                    public void run() {
//                        try {
//                            ObjectInputStream ois = null;
//                            try {
//                                ois = new ObjectInputStream(socket.getInputStream());
//                                String methodName = ois.readUTF();
//                                Class<?>[] parameterTypes = (Class<?>[]) ois.readObject();
//                                Object[] args = (Object[]) ois.readObject();
//                                ObjectOutputStream oos = null;
//                                try {
//                                    oos = new ObjectOutputStream(socket.getOutputStream());
//
//                                    Method method = service.getClass().getMethod(methodName, parameterTypes);
//                                    Object result = method.invoke(service, args);
//
//                                    oos.writeObject(result);
//
//                                }catch (Exception e){
//                                    e.printStackTrace();
//                                }finally {
//                                    oos.close();
//                                }
//
//                            } catch (IOException e) {
//                                e.printStackTrace();
//                            } finally {
//                                ois.close();
//                            }
//                        } catch (Exception e) {
//                            e.printStackTrace();
//                        }
//                    }
//                }).start();
//
//            } catch (Exception e) {
//                e.printStackTrace();
//            } finally {
//
//            }
//
//
//        }
//
//
//    }




    /**
     * 服务引用
     * @param interfaceClass
     * @param <T>
     * @return  返回代理对象
     */
    public  <T> T refer(Class<T> interfaceClass) {
        LOGGER.info("RpcFramework->referNetty() args: interfaceClass:{}, host:{}", interfaceClass.toString());
        String pkgName = interfaceClass.getPackage().getName();
        Optional<RpcClientProperties.RpcClient> optional =
                rpcClientProperties.getClients().values().stream().filter(n -> n.getBasePackages().equals(pkgName)).findFirst();
        if(!optional.isPresent()){
            //未配置rpc client异常
            throw new BusinessException();
        }
        RpcClientProperties.RpcClient rpcClient = optional.get();
        String interfaceName = interfaceClass.getName();
        Object rpcProxyObject = rpcProxyObjectMap.get(interfaceName);
        if(null != rpcProxyObject){
            LOGGER.info("RpcFramework->referNetty() args: interfaceClass:{}, rpcProxyObject get from map", interfaceClass.toString());
            return (T)rpcProxyObject;
        }
        T t = (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[]{interfaceClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                LOGGER.info("RpcFramework->referNetty() invoke: proxy:{}, method:{}, args:{}", proxy, method, args);

                if(Object.class.equals(method.getDeclaringClass())) {
                    try {
                        // 诸如hashCode()、toString()、equals()等方法，将target指向当前对象this
                        return method.invoke(this, args);
                    } catch (Throwable e) {
                        LOGGER.error("method execute Object method error..", e);
                    }
                }

                if("toString".equals(method.getName())) {
                    LOGGER.info("RpcFramework->referNetty() method is toString() called...");
                    return interfaceClass.toString();
                }

                RpcRequest request = new RpcRequest();
                request.setClassName(method.getDeclaringClass().getName());
                request.setMethodName(method.getName());
                request.setParameters(args);
                request.setParameterTypes(method.getParameterTypes());
                request.setId(UUID.randomUUID().toString());
                LOGGER.info("rpc request:{}", JSON.toJSONString(request));
                NettyClient nettyClient = new NettyClient(rpcClient.getIp(), rpcClient.getPort());
                try {
                    nettyClient.connect();
                } catch (Exception e) {

                }

                String returnTypeName = method.getReturnType().getName();
                boolean isJavaUtilList = "java.util.List".equals(returnTypeName);

                RpcResponse response = nettyClient.send(request);
                LOGGER.info("rpc request:{}, response:{}",
                        JSON.toJSONString(request),
                        JSON.toJSONString(response));
                if(0 != response.getErrorCode()){
                    //errorcode不为0，抛出异常
                    throw new BusinessException(response.getErrorCode(), response.getErrorMsg());
                }

                if(null == response){
                    LOGGER.info("rpc request:{} rpc response is null", JSON.toJSONString(request));
                    return null;
                }
                if(!isJavaUtilList) {
                    String resultJson = JSON.toJSONString(response.getResult());
                    return JSON.parseObject(resultJson, method.getReturnType());
                }
                return response.getResult();
            }
        });

        rpcProxyObjectMap.put(interfaceName, t);
        LOGGER.info("rpcProxyObjectMap:{}", rpcProxyObjectMap);
        return t;
    }
}
